package tv.acgn.advertisement.servicebase.advice;

import java.time.LocalDateTime;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import tv.acgn.advertisement.servicebase.annoation.IgnoreResponseAdvice;
import tv.acgn.advertisement.servicebase.exception.AbstractAdSysBusinessException;
import tv.acgn.advertisement.servicebase.vo.CommonResponse;

/**
 * @program: advertisement-ad-servicebase-base-advice
 * @description: 调用请求响应统一拦截器
 * 将所有网络请求的响应统一转换为 {@link CommonResponse} 格式
 *
 * 工作流程是在Service层处理完毕 真正返回给调用方之前
 * 处理Service层返回的对象
 * @see CommonResponse
 *
 * @author :junG
 * @date :2019-04-22 00:41
 **/
@RestControllerAdvice
@Slf4j
public class CommonResponseDataAdvice implements ResponseBodyAdvice {

    @Value("application.name")
    private String appName;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        /*
         * 如果一个类添加了忽略统一返回值处理 @IgnoreResponse 则不处理
         */
        if (returnType.getDeclaringClass().isAnnotationPresent(IgnoreResponseAdvice.class)) {
            return false;
        }
        /*
         * 如果一个方法添加了忽略统一返回值处理 @IgnoreResponse 则不处理
         */
        return null == returnType.getMethod() || !returnType.getMethod().isAnnotationPresent(
            IgnoreResponseAdvice.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {

        //TODO 通用返回统一添加 requestId通用参数

        CommonResponse commonResponse = CommonResponse.builder()
            .requestId("")
            .code(0)
            .success(true)
            .model(appName)
            .userId("")
            .build();
        /*
         * 如果未返回 内容
         * 一般是测试请求 直接返回 commonResponse
         * 但也可能是错误请求 记录下日志
         */
        if(null==body){
            log.debug("此次请求 返回为空引用 可能出现异常 commonResponse = {}", commonResponse);
            return commonResponse;
            /*
             * 如果返回数据 直接是 CommonResponse
             * 说明上层已经进行过自己的特殊处理
             *  则此处不处理 直接返回
             */
        } else if (body instanceof CommonResponse) {
            return ((CommonResponse)body).setModel(appName).setEndTime(LocalDateTime.now());
        } else if (body instanceof AbstractAdSysBusinessException) {
            /**
             *  如果 body 是 AbstractAdSysBusinessException 的子类
             *  说明调用发生异常
             *  设置调用状态为失败
             *  设置状态码为1 表示为未处理的服务内部异常
             *  设置异常内容体
             *
             *  防御性内容 正常路径
             *  业务异常会被 {@link GlobalExceptionResolverAdvice}捕获
             *  不会进入此分支
             */
            log.error("程序运行出现业务异常 {}", body);
            commonResponse.setSuccess(false);
            commonResponse.setCode(1);
            commonResponse.setException(body);
        } else {
            /*
             * 调用成功
             * 设置返回内容体
             */
            commonResponse.setData(body);
        }
        return commonResponse.setEndTime(LocalDateTime.now());
    }
}
